/**
 * @brief MCU SDRAM 驱动
 * @author 张勇 41204@qq.com
 * @date 2022-04
 */
 
#pragma once

#include <stdint.h>
#include "stm32f4xx.h"
#include "share/std_err.h"
#include "_mcu_gpio.h"

/// @brief 用于定义 SDCKE/SDNE 引脚复用关系配置函数的宏
#define _SDRAM_FPIN_MUX(FPIN, PIN) \
    static inline mcu_gpio_num_t SDRAM_##FPIN##_USE_##PIN() { return mcu_gpio_pinmux_config(MCU_GPIO_NUM_##PIN, LL_GPIO_AF_12) ? MCU_GPIO_NUM_MAX : MCU_GPIO_NUM_##PIN; }

_SDRAM_FPIN_MUX(CKE0, PC3);
_SDRAM_FPIN_MUX(CKE0, PH2);
_SDRAM_FPIN_MUX(CKE1, PB5);
_SDRAM_FPIN_MUX(CKE1, PH7);
_SDRAM_FPIN_MUX(NE0,  PC2);
_SDRAM_FPIN_MUX(NE0,  PH3);
_SDRAM_FPIN_MUX(NE1,  PB6);
_SDRAM_FPIN_MUX(NE1,  PH6);
_SDRAM_FPIN_MUX(NWE,  PC0);
_SDRAM_FPIN_MUX(NWE,  PH5);

// SDRAM 模式寄存器参数
// bit0~bit2 为指定突发访问的长度(可以是1/2/4/8)
#define SDRAM_MODEREG_BURST_LENGTH_1             ((uint16_t)0x0000)
#define SDRAM_MODEREG_BURST_LENGTH_2             ((uint16_t)0x0001)
#define SDRAM_MODEREG_BURST_LENGTH_4             ((uint16_t)0x0002)
#define SDRAM_MODEREG_BURST_LENGTH_8             ((uint16_t)0x0004)
// bit3 为指定突发访问的类型
#define SDRAM_MODEREG_BURST_TYPE_SEQUENTIAL      ((uint16_t)0x0000) // 0,连续
#define SDRAM_MODEREG_BURST_TYPE_INTERLEAVED     ((uint16_t)0x0008) // 1,交错
// bit4~bit6 为CAS值(可以是2/3)
#define SDRAM_MODEREG_CAS_LATENCY_2              ((uint16_t)0x0020)
#define SDRAM_MODEREG_CAS_LATENCY_3              ((uint16_t)0x0030)
// bit7和bit8为运行模式
#define SDRAM_MODEREG_OPERATING_MODE_STANDARD    ((uint16_t)0x0000) // 0,标准模式
// bit9为指定的写突发模式
#define SDRAM_MODEREG_WRITEBURST_MODE_PROGRAMMED ((uint16_t)0x0000)
#define SDRAM_MODEREG_WRITEBURST_MODE_SINGLE     ((uint16_t)0x0200) // 1,单点访问
// bit10和bit11位保留位

// SDRAM 初始化指令
typedef struct _mcu_sdram_init_command {    
    uint32_t CommandMode;            /// @see FMC_SDRAM_CommandTypeDef.FMC_SDRAM_Command_Mode, 如果>7，则表示指令无效(用于指示指令列表结束)
    uint32_t AutoRefreshNumber;      /// @see FMC_SDRAM_CommandTypeDef.FMC_SDRAM_Command_Target
    uint32_t ModeRegisterDefinition; /// @see FMC_SDRAM_CommandTypeDef.AutoRefreshNumber
    uint32_t PostWaitUs;             /// 本指令执行之后至少延时多少 us 才能执行下一条指令
} mcu_sdram_init_command_t;

typedef struct _mcu_sdram {
    // SDRAM 句柄
    // !!! 注意大坑: 这里必须把 sdram 定义为静态变量，或全局变量 !
    // 如果定义为局部变量，则会出现很奇葩的问题: 只有 &sdram = 0x20001FF8 时初始化正常，其它的地址都会初始化失败 !!!
    SDRAM_HandleTypeDef hsdram;
    // SDRAM 时序参数
    FMC_SDRAM_TimingTypeDef timing;
    // 模式寄存器配置
    uint16_t modereg;
    // 刷新频率
    uint32_t refresh_rate;
    // SDRAM 开始地址
    uint32_t base_addr;
    // SDRAM 大小
    uint32_t size;
    // SDRAM 初始化指令序列，最后一条指令为 -1(0xFFFFFFFF)，则表示指令列表结束
    const mcu_sdram_init_command_t *initcmds;
} mcu_sdram_t;

// @brief 初始化 SDRAM
std_err_t mcu_sdram_init(mcu_sdram_t *sdram, bool test);

// @brief 测试 SDRAM
std_err_t mcu_sdram_test(mcu_sdram_t *sdram);
std_err_t mcu_sdram_test_aligns(mcu_sdram_t *sdram);
